from django.shortcuts import render, redirect
from QQLoginTool.QQtool import OAuthQQ
from django.views import View
from django.http import JsonResponse, HttpResponse, HttpResponseForbidden,  HttpResponseServerError
from django.contrib.auth import login
import re
from django_redis import get_redis_connection
import requests
from urllib.parse import urlencode

from medo_mall.utils.response_code import RETCODE
from .models import OAuthQQUser, OAuthSinaUser
from django.conf import settings
from .utils import generate_openid_signature, check_openid
from users.models import User
from carts.utils import merge_cart_cookie_to_redis

import logging
logger = logging.getLogger('django')


class QQAuthURLView(View):
    '''提供QQ登入url'''
    def get(self, request):
        # 1.获取查询参数
        next = request.GET.get('next') or '/'

        # 2.创建OAuthQQ 工具对象
        oauth = OAuthQQ(
            client_id=settings.QQ_CLIENT_ID,
            client_secret=settings.QQ_CLIENT_SECRET,
            redirect_uri=settings.QQ_REDIRECT_URI,
            state=next
        )

        # 3.调用它里面的get_qq_url方法 得到拼接好的qq登入url
        login_url = oauth.get_qq_url()
        # 4.响应
        return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'login_url': login_url})


class QQAuthView(View):
    def get(self, request):
        # 1.获取查询字符串code
        code = request.GET.get('code')

        # 2.校验是否回去到code
        if code is None:
            return HttpResponseForbidden('缺少code')

        # 3.向创建QQ 登录辅助工具对象
        oauth_qq = OAuthQQ(
            client_id=settings.QQ_CLIENT_ID,
            client_secret=settings.QQ_CLIENT_SECRET,
            redirect_uri=settings.QQ_REDIRECT_URI,
        )
        try:
            # 4.调用 get_access_token(code) 得到access_token
            access_token = oauth_qq.get_access_token(code)

            # 5.调用get_open_id(access_token) 得到openid
            openid = oauth_qq.get_open_id(access_token)
        except Exception as e:
            logger.error(e)
            return HttpResponseServerError('OAuth 2.0认证失败')

        # 6. 查询opneid
        try:
            oAuth_modil = OAuthQQUser.objects.get(openid=openid)
            # 查询到openid说次QQ用户已绑定美多用户，代表登入成功
            user = oAuth_modil.user
            # 状态保持
            login(request,user)
            next = request.GET.get('state') or '/'
            # 向浏览器的cookie中存储username
            response = redirect(next)
            response.set_cookie('username', user.username, max_age=settings.SESSION_COOKIE_AGE)

            # 登录成功合并购物车
            merge_cart_cookie_to_redis(request, response)
            # 重定向到来源界面
            return response

        except OAuthQQUser.DoesNotExist:
            # 说明openid还没有绑定美多用户，渲染一绑定页面
            # 对openid进行加密
            openid = generate_openid_signature(openid)
            return render(request, 'oauth_callback.html', {"openid": openid})

    def post(self, request):
        """openid绑定用户逻辑"""

        # 1.接收表单数据
        query_dict = request.POST
        mobile = query_dict.get('mobile')
        password = query_dict.get('password')
        sms_code = query_dict.get('sms_code')
        openid_sign = query_dict.get('openid')

        # 2.校验
        if all([mobile, password, sms_code, openid_sign]) is False:
            return HttpResponseForbidden('缺少必传参数')

        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return HttpResponseForbidden('请输入8-20位的密码')
        if not re.match(r'^1[345789]\d{9}$', mobile):
            return HttpResponseForbidden('请输入正确的手机号码')

        # 创建redis连接对象
        redis_conn = get_redis_connection('verify_codes')

        # 获取redis数据库中当前用户短信验证码
        sms_code_server_bytes = redis_conn.get('sms_%s' % mobile)

        # 删除已经取出的短信验证码,让它只能被用一次
        redis_conn.delete('sms_%s' % mobile)

        # 判断redis中短信验证码过期
        if sms_code_server_bytes is None:
            return JsonResponse({'code': RETCODE.SMSCODERR, 'errmsg': '短信验证码已过期'})

        # 将bytes类型转换为字符串类型
        sms_code_server = sms_code_server_bytes.decode()

        # 用户填写的和redis中的短信验证码是否一致
        if sms_code != sms_code_server:
            return JsonResponse({'code': RETCODE.SMSCODERR, 'errmsg': '短信验证码填写错误'})

        # 对openid进行解密
        openid = check_openid(openid_sign)
        if openid is None:
            return HttpResponseForbidden('openid无效')

        # 3.根据手机号字段查询user表
        try:
            # 3.1 如果通过mobile字段查询到用户  说明此用户是已有账号的美多老用户
            user = User.objects.get(mobile=mobile)

            # 3.1.1 如果是老用户还要检验用户密码是否正确
            if user.check_password(password) is False:
                return HttpResponseForbidden('老用户信息填写不正确')
        except User.DoesNotExist:

            # 3.2 如果没有查询到用户，说明他是一个美多新用户
            # 3.3 如果是新用户，就常见一个新的user 用户名就用mobile
            user = User.objects.create_user(username=mobile, password=password, mobile=mobile)

        # 4. 创建OAuthQQUser 新的记录保存openid 及 user
        OAuthQQUser.objects.create(
            openid=openid,
            user=user,
        )

        # 5. 状态保持
        login(request, user)
        # 获取state 界面来源
        next = request.GET.get('state') or '/'
        # 创建响应对象
        response = redirect(next)
        response.set_cookie('username', user.username, max_age=settings.SESSION_COOKIE_AGE)

        # 登录成功合并购物车
        merge_cart_cookie_to_redis(request, response)

        # 6. 重定向到指定来源界面
        return response


class SinaAuthURLView(View):
    """提供Sina登入url"""
    def get(self, request):
        # 1.获取查询参数
        next = request.GET.get('next') or '/'

        # 2.创建Sina Url参数组建
        data_oauth = {
            'client_id': settings.SINA_CLIENT_ID,
            'redirect_uri': settings.SINA_REDIRECT_URI,
            'state': next,
            'response_type': 'code'
        }

        # 构建url
        sina_url = 'https://api.weibo.com/oauth2/authorize?' + urlencode(data_oauth)
        print(sina_url)

        # 3.调用它里面的get_qq_url方法 得到拼接好的qq登入url
        # login_url = oauth.get_qq_url()
        # 4.响应
        return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'login_url': sina_url})


class SinaAuthView(View):
    def get(self, request):
        # 1.获取查询字符串code
        code = request.GET.get('code')

        # 2.校验是否回去到code
        if code is None:
            return HttpResponseForbidden('缺少access_token')

        # 3.创建Sian Url参数组建
        data_oauth = {
            'client_id': settings.SINA_CLIENT_ID,
            'client_secret': settings.SINA_CLIENT_SECRET,
            'grant_type': 'authorization_code',
            'code': code,
            'redirect_uri': settings.SINA_REDIRECT_URI,

        }

        # 构建url
        sina_url = 'https://api.weibo.com/oauth2/access_token?' + urlencode(data_oauth)


        # 发送请求
        try:
            response = requests.post(sina_url)
            print(response)

            # 提取数据
            data = response.text
            print(data)

            # 转化为字典
            data = eval(data)

        except:
            raise Exception('Sina请求失败')

        try:
            # 4.得到access_token
            uid = data.get('access_token')
            print(type(uid))
            print(uid)

        except Exception as e:
            logger.error(e)
            return HttpResponseServerError('OAuth 2.0认证失败')

        # 5. 查询uid
        try:
            sina_model = OAuthSinaUser.objects.get(uid=uid)
            # 查询到uid说此sina用户已绑定美多用户，代表登入成功
            user = sina_model.user
            # 状态保持
            login(request, user)
            next = request.GET.get('state') or '/'
            # 向浏览器的cookie中存储username
            response = redirect(next)
            response.set_cookie('username', user.username, max_age=settings.SESSION_COOKIE_AGE)

            # 登录成功合并购物车
            merge_cart_cookie_to_redis(request, response)
            # 重定向到来源界面
            return response

        except OAuthSinaUser.DoesNotExist:
            # 说明openid还没有绑定美多用户，渲染一绑定页面
            return render(request, 'sina_oauth_callback.html', {"uid": uid})

    def post(self, request):
        """uid绑定用户逻辑"""

        # 1.接收表单数据
        query_dict = request.POST
        mobile = query_dict.get('mobile')
        password = query_dict.get('password')
        sms_code = query_dict.get('sms_code')
        uid = query_dict.get('uid')

        # 2.校验
        if all([mobile, password, sms_code, uid]) is False:
            return HttpResponseForbidden('缺少必传参数')

        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return HttpResponseForbidden('请输入8-20位的密码')
        if not re.match(r'^1[345789]\d{9}$', mobile):
            return HttpResponseForbidden('请输入正确的手机号码')

        # 创建redis连接对象
        redis_conn = get_redis_connection('verify_codes')

        # 获取redis数据库中当前用户短信验证码
        sms_code_server_bytes = redis_conn.get('sms_%s' % mobile)

        # 删除已经取出的短信验证码,让它只能被用一次
        redis_conn.delete('sms_%s' % mobile)

        # 判断redis中短信验证码过期
        if sms_code_server_bytes is None:
            return JsonResponse({'code': RETCODE.SMSCODERR, 'errmsg': '短信验证码已过期'})

        # 将bytes类型转换为字符串类型
        sms_code_server = sms_code_server_bytes.decode()

        # 用户填写的和redis中的短信验证码是否一致
        if sms_code != sms_code_server:
            return JsonResponse({'code': RETCODE.SMSCODERR, 'errmsg': '短信验证码填写错误'})

        # 3.根据手机号字段查询user表
        try:
            # 3.1 如果通过mobile字段查询到用户  说明此用户是已有账号的美多老用户
            user = User.objects.get(mobile=mobile)

            # 3.1.1 如果是老用户还要检验用户密码是否正确
            if user.check_password(password) is False:
                return HttpResponseForbidden('老用户信息填写不正确')
        except User.DoesNotExist:

            # 3.2 如果没有查询到用户，说明他是一个美多新用户
            # 3.3 如果是新用户，就常见一个新的user 用户名就用mobile
            user = User.objects.create_user(username=mobile, password=password, mobile=mobile)

        # 4. 创建OAuthQQUser 新的记录保存openid 及 user
        OAuthSinaUser.objects.create(
            uid=uid,
            user=user,
        )

        # 5. 状态保持
        login(request, user)
        # 获取state 界面来源
        next = request.GET.get('state') or '/'
        # 创建响应对象
        response = redirect(next)
        response.set_cookie('username', user.username, max_age=settings.SESSION_COOKIE_AGE)

        # 登录成功合并购物车
        merge_cart_cookie_to_redis(request, response)

        # 6. 重定向到指定来源界面
        return response

